#include "audiomanager.h"
#include "resourcemanager.h"

AudioManager* AudioManager::m_self=0;

AudioManager::AudioManager():music(NULL),num_channels(0),enable_music(false),
									enable_sound(false)
{
	m_self=this;
    m_initialized = false;
}

AudioManager::~AudioManager()
{
	m_self=0;
}

AudioManager& AudioManager::self()
{
	if(m_self==0)
		new AudioManager();

	return (*m_self);
}

bool AudioManager::exists()
{
	return m_self!=0;
}

bool AudioManager::init(int n_chan,int freq,Uint16 format,
		int stereo,int chunk)
{
	if(SDL_InitSubSystem(SDL_INIT_AUDIO)<0)
		return -1;
	if(Mix_OpenAudio(freq,format,stereo,chunk)<0)
		return -1;

	loadChannels(n_chan);
	volume=128;

	enable_sound=true;
    enable_music=true;
    m_initialized = true;
	return 0;
}

void AudioManager::update()
{
	if(!enable_sound)
		return;

	for(int i=0;i<num_channels;i++)
		channels[i]->update();
}

void AudioManager::shutdown()
{
	disableSound();
	disableMusic();
	for(unsigned i=0;i<chunks.size();i++)
		delete chunks[i];
	chunks.clear();

	Mix_CloseAudio();
	SDL_QuitSubSystem(SDL_INIT_AUDIO);
}

Sound* AudioManager::loadSound(string soundfile)
{
	//If the sound is already opened, we'll pass the sound
	//and we'll increment the number of users
	unsigned i;
    soundfile=ResourceManager::self()["sound"] + soundfile;
	for(i=0;i<chunks.size();i++)
	{
		if(chunks[i]->name==soundfile)
		{
			chunks[i]->num_users++;
			break;
		}
	}
	//If we couldn't find it, load the sound
	if(i==chunks.size())
	{
		//we look for a chunk free
		for(i=0;i<chunks.size();i++)
		{
			if(chunks[i]->num_users==0)
			{
				chunks[i]->name=soundfile;
				chunks[i]->num_users=1;
                chunks[i]->chunk=Mix_LoadWAV(soundfile.c_str());
				break;
			}
		}
		//if there isn't any, create one
		if(i==chunks.size())
		{
			Chunk *ch=new Chunk();
			ch->name=soundfile;
			ch->num_users=1;
            ch->chunk=Mix_LoadWAV(soundfile.c_str());

			chunks.push_back(ch);
		}
	}

	Sound* sound=new Sound(i);
	return sound;
}

void AudioManager::unloadSound(Sound* s)
{
	if(chunks[s->getID()]->num_users>1)
		chunks[s->getID()]->num_users--;
	else
	{
		Mix_FreeChunk(chunks[s->getID()]->chunk);
		chunks[s->getID()]->name="";
		chunks[s->getID()]->num_users=0;
	}

	//Delete the last unused chunks
	while(chunks[chunks.size()-1]->num_users==0)
	{
		delete chunks[chunks.size()-1];
		chunks.pop_back();
	}

	delete s;
}

void AudioManager::playSound(Sound *s)
{
	if(!enable_sound || !m_initialized)
		return;
	for(int i=0;i<num_channels;i++)
	{
		if(channels[i]->isFree())
		{
			channels[i]->playSound(s,chunks[s->getID()]->chunk);
			break;
		}
	}
}


///////////
//MUSIC
///////////

int AudioManager::loadMusic(string musicname)
{
	if(music!=NULL || !m_initialized)
		unloadMusic();
    string name=ResourceManager::self()["sound"]+musicname;
	music=Mix_LoadMUS((const char*)name.c_str());
	if(music==NULL)
		return -1;
    
    m_musicName = musicname;
	return 0;
}

void AudioManager::unloadMusic()
{
    Mix_HaltMusic();
    Mix_FreeMusic(music);
    music=NULL;
    m_musicName = string();
}

string AudioManager::getMusicName()
{
    if(!enable_music || !m_initialized || music==NULL)
        return "";
    
    return m_musicName;
    
}

void AudioManager::startMusic(bool once)
{
    if(!enable_music || !m_initialized || music==NULL)
        return;
    
    int times = (once) ? 1 : -1;
    
    Mix_PlayMusic(music, times);
}

void AudioManager::startMusic(int ms)
{
	if(!enable_music || !m_initialized || music==NULL)
		return;
	if(ms==0)
		Mix_PlayMusic(music,-1);
	else
		Mix_FadeInMusic(music,-1,ms);
}

void AudioManager::stopMusic(int ms)
{
	if(!enable_music || !m_initialized)
		return;
	if(ms==0)
		Mix_HaltMusic();
	else
		Mix_FadeOutMusic(ms);
}

void AudioManager::loadChannels(int ch)
{
	if(enable_sound)
		return;
	num_channels=Mix_AllocateChannels(ch);
	channels=new Channel*[num_channels];
	for(int i=0;i<num_channels;i++)
		channels[i]=new Channel(i);
}

void AudioManager::unloadChannels()
{
	if(!enable_sound || !m_initialized)
		return;
	Mix_HaltChannel(-1);
	for(int i=0;i<num_channels;i++)
		delete channels[i];
	delete[] channels;
	num_channels=0;
}

void AudioManager::loadAllChunks()
{
	if(enable_sound)
		return;
	for(unsigned i=0;i<chunks.size();i++)
	{
		if(chunks[i]->num_users>0)
			chunks[i]->chunk=Mix_LoadWAV(chunks[i]->name.c_str());
	}
}

void AudioManager::unloadAllChunks()
{
	if(!enable_sound || !m_initialized)
		return;
	for(unsigned i=0;i<chunks.size();i++)
	{
		if(chunks[i]->num_users>0)
			Mix_FreeChunk(chunks[i]->chunk);
	}
}

void AudioManager::setSoundEnabled(bool enabled)
{
    if(!m_initialized)
        return;
    
    if(enabled)
        enableSound();
    else
        disableSound();
}

void AudioManager::setMusicEnabled(bool enabled)
{    
    if(!m_initialized)
        return;

    if(enabled)
        enableMusic();
    else
        disableMusic();
}
